In [1]:
from IPython.display import display
from IPython.display import HTML
import IPython.core.display as di # Example: di.display_html('<h3>%s:</h3>' % str, raw=True)

# This line will hide code by default when the notebook is exported as HTML
di.display_html('<script>jQuery(function() {if (jQuery("body.notebook_app").length == 0) { jQuery(".input_area").toggle(); jQuery(".prompt").toggle();}});</script>', raw=True)

# This line will add a button to toggle visibility of code blocks, for use with the HTML export version
di.display_html('''<button onclick="jQuery('.input_area').toggle(); jQuery('.prompt').toggle();">Toggle code</button>''', raw=True)
In [1]:
%qtconsole
In [2]:
#2019/03/28 C1S04_WFS_stress_compensation_90mm from C1S04_WFS_stress_compensation_90mm, 
# made together, to verify old results. Eventually used also for the 70 mm version.
# note that stress_compensation functions were completely changed before this notebook.
# rms_roi column was changed to fom_roi, and all functions were adapted to work on a generic
# fom that accept data,x,y. The change of label breaks compatibility, so all previous 
# notebooks will not work. 
# Also this notebook, crop nans from data and align corners.
# The notebook was tested with old values, showing that the alignment of corners (and
#    the changes in optimization code) are irrelevant. Results are not visualized here
#    but are saved in 'VK_reproduce'. All changes are due to the new crop,
#    numbers are still not far from previous, just a bit smaller.

#2019/03/27 PCO1S24_PZT_stress_compensation from C1S04_PZT_stress_compensation
#  Here stress needed to calculate compensating stress for ideal shape of 0.5 um axial sag is calculated.
#  Calculation is made on actual shape after PZT deposition.
#  Part was gold coated at PSU.

%reset
#pylab qt -> open windows, needs display
%pylab qt 
%load_ext autoreload
%autoreload 2
Once deleted, variables cannot be recovered. Proceed (y/[n])? y
Populating the interactive namespace from numpy and matplotlib
In [3]:
#from pySurf.fit_cylinder import *
#from pySurf.points import *
from pySurf.points import plot_points,matrix_to_points2
from pySurf.points import subtract_points,level_points
from pySurf.points import points_find_hull,crop_points
from pySurf.psd2d import calculatePSD
from pySurf.points import points_autoresample
#from calibrate_align import *
from pyProfile.profile import polyfit_profile
from plotting.multiplots import compare_images
from dataIO.fn_add_subfix import fn_add_subfix
from dataIO.span import span
#from PSDanalysis import *
from pySurf.instrumentReader import matrix4D_reader
import os
from pySurf.instrumentReader import bin_reader
from plotting.add_clickable_markers import add_clickable_markers2
from plotting.backends import maximize
In [5]:
from pySurf.instrumentReader import points_reader
from pySurf.affine2D import plot_transform,find_rototrans,find_affine
from pySurf.distanceTable import distanceTable
from pySurf.data2D_class import Data2D
from pySurf.data2D import data_from_txt
from dataIO.outliers import remove_outliers
import pandas as pd
In [ ]:
pwd
In [6]:
from pySurf.psd2d import calculatePSD
from utilities.imaging import fitting as fit
In [54]:
from scripts.stress_compensation import comp2, scale_fit2, trioptimize, fom_rms as fom, fom_sl
from pySurf.instrumentReader import fitsWFS_reader
In [8]:
correct_misalignment=fit.fitConeMisalign

Determination of PZT stress

WFS Data

Aligned and registered sample figure are loaded from PZT stress fit analysis.

In [161]:
outfolder=r'results\C1S04_WFS_stress_compensation_90mm'  #set output
In [162]:
allres=pd.DataFrame()  #will contain all results

After PZT deposition

In [163]:
wf=r'results\C1S04_PZT_stress_fit\wfs_substrate_aligned.dat'

p2=Data2D(file=wf,reader=data_from_txt,delimiter=' ',units=['mm','mm','$\mu$m'],
         name='C1S04 PZT WFS data',addaxis=1,center=(0,0))

plt.clf()
p2.plot()

display(plt.gcf())

p2.save(os.path.join(outfolder,fn_add_subfix(wf,'','.dat',strip=True)))
plt.savefig(os.path.join(outfolder,fn_add_subfix(wf,'','.png',strip=True)))
In [109]:
from pySurf.data2D import add_clickable_markers2

ax=add_clickable_markers2()
cids established:
 [5, 6, 7]
In [166]:
print(ax.markers)
[[-50.91535317274073, 49.808209478596886], [-53.07634713026975, -47.97676709959098], [49.84099009704956, -49.32738832304663], [52.54223254396083, 47.647215521067885]]
In [164]:
m1=[[-50.91535317274073, 49.808209478596886], 
    [-53.07634713026975, -47.97676709959098], 
    [49.84099009704956, -49.32738832304663], 
    [52.54223254396083, 47.647215521067885]]
m2=[[-50.8,50.8],[-50.8,-50.8],[50.8,-50.8],[50.8,50.8]]
trans=find_affine(m1,m2)
In [170]:
plt.figure(1)
plt.clf()
p1=p2.apply_transform(trans)
p1=p1.crop([-50.8,50.8],[-50.8,50.8])
p1.level().plot()
display(plt.gcf())
In [171]:
#test, This reproduces exactly the previous case of 90 mm optimization, showing that 
# alignment has no relevance, but crop does.
#plt.figure(1)
#plt.clf()
#p1=p2
##p1=p1.crop([-50.8,50.8],[-50.8,50.8])
#p1.level().plot()
#display(plt.gcf())
In [172]:
from pySurf.testSurfaces import make_sag
s1=Data2D(make_sag(*(p1.data.shape[::-1]))[0]*0.5,p1.x,p1.y,name='Target Shape',units=p1.units)

Figures below show the initial and target shape and the initial deviation that needs to be corrected in form of opposite stress, as surface and as central axial profiles.

In [173]:
#calculate deviation from ideal shape
plt.figure()
maximize()

ax1=plt.subplot(131)
p1.level((1,1)).plot()

ax2=plt.subplot(132,sharex=ax1,sharey=ax1)
s1.level((1,1)).plot()

ax3=plt.subplot(133,sharex=ax1,sharey=ax1)
sdiff=(p1-s1).level((1,1))
sdiff.name='Initial deviation from target shape'
sdiff.plot()
plt.clim([-1,1])

display(plt.gcf())
In [174]:
p1.save(os.path.join(outfolder,'wfs_PZT_aligned.dat'))
s1.save(os.path.join(outfolder,'target_shape.dat'))
sdiff.save(os.path.join(outfolder,'figure_change_aligned.dat'))

plt.savefig(os.path.join(outfolder,'initial_deviation.png'))
In [175]:
from pyProfile.profile import level_profile, subtract_profiles
from pySurf.points import extract_profile

## Extract axial profile
x1,y1 = level_profile(*(extract_profile(p1.topoints(),(0,-45),(0,45))))
x2,y2 = level_profile(*(extract_profile(s1.topoints(),(0,-45),(0,45))))
xd,yd = level_profile(*subtract_profiles(x2,y2,x1,y1))

plt.figure()
plt.plot(x1,y1,label='PZT WFS Data')
plt.plot(x2,y2,label='Target Shape')
plt.plot(xd,yd,label='Optimal Compensation')
plt.xlabel(p1.units[0])
plt.ylabel(p1.units[2])
plt.grid(1)
plt.ylim([-0.8,0.8])
plt.legend(loc=0)
plt.title('central axial profiles')
display(plt.gcf())
In [176]:
plt.savefig(os.path.join(outfolder,'data_central_profile.png'))

Fit PZT compensating stress with VK's uniform stress simulation

In [178]:
prfolder='fit_VK'
os.makedirs(os.path.join(outfolder,prfolder),exist_ok=True)

Compensating stress is calculated starting from Vladimir Kradinov's simulation 2018/06/19 corresponding to a coating with uniform integrated stress 300 MPa um.

The stress matching deviation of surface from target value is determined by finding the best fit scale coefficient $s$ that minimizes the rms of surface difference:

$S_\textrm{WFS} - s S_{target}$

This calculation is shown below alone and in comparison with data. A stress opposite in sign will bring the sample surface to a shape that is close to target value.

In [179]:
from pySurf.instrumentReader import points_reader, fitsWFS_reader
from pySurf.affine2D import plot_transform,find_rototrans
from pySurf.distanceTable import distanceTable
from pySurf.data2D_class import Data2D
from pySurf.data2D import resample_data
In [250]:
FEAf=r'G:\My Drive\Shared by Vincenzo\Metrology logs and data\Simulations\Coating_stress\VK20180619\coat_300_MPa_um_ur.fits'  #300 MPa*um load
In [251]:
plt.figure(2)
plt.clf()

#manually adjust x and y scales to 4 in, adjsut units and signs.

fdata,xf,yf=fitsWFS_reader(FEAf)  #can accept ypix, ytox,..
#xf,yf,fdata=yf,xf,fdata.T

fdata=fdata-correct_misalignment(fdata)[0]
fdata=-fdata*1000000.  #m to um  
#fitsWFS_reader makes a mysterious change of sign, that is good with WK's
#data that are bump negative conversion.

#scale and center axes (100 mm square image size)
nx=fdata.shape[0]
scale=101.6/nx
xf=(xf-span(xf).sum()/2)*scale
yf=(yf-span(yf).sum()/2)*scale

df=Data2D(fdata,xf,yf,units=['mm','mm','$\mu$m'],name='VK Simulation (cone)')
#rule is: corners have same sign as delta Radius

plt.clf()
df.plot()
display(plt.gcf())
df.save(fn_add_subfix(FEAf,'','.dat',strip=True,pre=outfolder+os.path.sep))
plt.savefig(fn_add_subfix(FEAf,'','.png',strip=True,pre=outfolder+os.path.sep))
In [252]:
diff=sdiff-df  

plt.figure()
maximize()
ax1=plt.subplot(131)
sdiff.plot()

ax2=plt.subplot(132,sharex=ax1,sharey=ax1)
df.plot()

ax3=plt.subplot(133,sharex=ax1,sharey=ax1)
diff.plot()

display(plt.gcf())
G:\My Drive\libraries\python\userKov3\pySurf\data2D.py:671: RuntimeWarning: invalid value encountered in greater
  data[((data>zrange[1]) | (data<zrange[0]))]=np.nan
G:\My Drive\libraries\python\userKov3\pySurf\data2D.py:671: RuntimeWarning: invalid value encountered in less
  data[((data>zrange[1]) | (data<zrange[0]))]=np.nan
In [253]:
plt.savefig(os.path.join(outfolder,prfolder,'starting_data.png'))

DO FIT

In [254]:
plt.close('all')
In [255]:
outfolder
Out[255]:
'results\\C1S04_WFS_stress_compensation_90mm'

Fit results

The best fit for stress is determined by analytical formula for simple linear regression (intercept coefficient = 0). The fit is repeated for different azimuthal apertures. For each of them it is shown:

  1. Plot of surfaces and their difference on the selected region of interest (ROI)
  2. Plot of the entire surface full aperture
  3. Numerical value of best fit scale parameter and FOM of residuals

Each plot reports values for the ROI (small box) and for the entire surface (larger box) (the two are identical for the crop image at point 2)

Used functions (for who is interested in details).

In [256]:
print(help(trioptimize))
print('---------------------------')
print(help(scale_fit2))
print('---------------------------')
print(help(fom))
print('---------------------------')
print(help(fom_sl))
Help on function trioptimize in module scripts.stress_compensation:

trioptimize(d1, d2, fom, testvals=None, rois=None, outfile=None, noplot=False, dis=True)
    run optimizations using scale_fit2 over a set of cases
    defined by the values in matching lists of testvals (array of scaling test values) 
    and rois ([[xmin,xmax],[ymin,ymax]]). 
    
    Returns a pandas data structure with parameters and best scaling values for each case,
        with fields ['tbest','testval','roi','fom_roi'].
    
    Plot 
    
    d1 and d2 are Data2D objects.
    testvals: list of vectors to use as test values for each of the ROI
    rois:    list of rois (same len as testvals), in form [[xmin,xmax],[ymin,ymax]], 
        set to None to use for range.
    outfile:
    noplot:
    dis:

None
---------------------------
Help on function scale_fit2 in module scripts.stress_compensation:

scale_fit2(d1, d2, testval=None, fom=<function fom_rms at 0x000002C9A4B211E0>, outfile='', roi=None, crop=False, dis=True)
    d1 and d2 are Data2D objects. Returns value for scaling tbest of d2
    that minimizes fom(d1-d2*tbest). e.g. fom = rms of surface/slope.
    Plot rms as a function of scale factor (if testval are passed for raster scan) 
        and 4 panel plot of best result (use comp2) either cropped (if crop is True) 
        or full.
    
    if testval is set to None, best fit is determined by analytical formula (minimizing
        rms of surface difference). If tesval is array, all values are tested. 
    
    if `roi` is passed, fom is calculated on the roi only.
    `crop` flag controls the plotting. If True, only the roi is plotted and 
    color scale is adjusted accordingly. Otherwise, entire region is plotted and
    a rectangle around the roi is plotted.
    dis=True print information and display plots in notebook.
    
    all fom and stats are calculated on leveled data, ideally I want to have stats in 
    roi unleveled, but it is safer this way for now.

None
---------------------------
Help on function fom_rms in module scripts.stress_compensation:

fom_rms(tdata, x=None, y=None)
    Fit surface after plane level

None
---------------------------
Help on function fom_sl in module scripts.stress_compensation:

fom_sl(tdata, x=None, y=None)
    Fit slope of surface after plane level

None
In [257]:
allres=pd.DataFrame()
In [258]:
os.makedirs(os.path.join(outfolder,prfolder),exist_ok=True)
In [259]:
plt.close('all')

#initialize allres
scales=trioptimize(sdiff,df,fom=fom,
           rois=[[[-10,10],[-45,45]],[[-20,20],[-45,45]],[[-30,30],[-45,45]],
                 [[-37,37],[-45,45]],None],dis =True,
                  outfile=os.path.join(outfolder,prfolder,'VK_stress_fit'))
allres=allres.append(scales)


===   CASE 1: VK_stress_fit_crop01  ===

CROP: [-10.00:10.00] [-45.00:45.00]
TEST RANGE: [None None]
INITIAL FOM: 0.694973904707699
G:\My Drive\libraries\python\userKov3\dataIO\outliers.py:28: RuntimeWarning: invalid value encountered in less
  mask=mask & (np.abs(data-np.nanmean(data))<(nsigma*sigma))
BEST SCALE: 0.038  FOM: 0.2504840124222633



===   CASE 2: VK_stress_fit_crop02  ===

CROP: [-20.00:20.00] [-45.00:45.00]
TEST RANGE: [None None]
INITIAL FOM: 0.7447696811585148
BEST SCALE: -0.066  FOM: 0.4934221748300909



===   CASE 3: VK_stress_fit_crop03  ===

CROP: [-30.00:30.00] [-45.00:45.00]
TEST RANGE: [None None]
INITIAL FOM: 0.7911639746810762
BEST SCALE: 0.601  FOM: 0.7552416563578891



===   CASE 4: VK_stress_fit_crop04  ===

CROP: [-37.00:37.00] [-45.00:45.00]
TEST RANGE: [None None]
INITIAL FOM: 0.8029137188701998
BEST SCALE: 0.868  FOM: 0.7953590986581552



===   CASE 5: VK_stress_fit_crop05  ===

FULL APERTURE
TEST RANGE: [None None]
INITIAL FOM: 0.845737956487664
BEST SCALE: 1.025  FOM: 0.8440193362855167

===
SUMMARY:
CROP: [-10.00:10.00] [-45.00:45.00] --> BEST SCALE:  0.03769517506921266
CROP: [-20.00:20.00] [-45.00:45.00] --> BEST SCALE:  -0.06642458870755344
CROP: [-30.00:30.00] [-45.00:45.00] --> BEST SCALE:  0.6010987788225592
CROP: [-37.00:37.00] [-45.00:45.00] --> BEST SCALE:  0.8676633254742938
FULL APERTURE--> BEST SCALE:  1.0246018419416876
=======================
In [260]:
scales[['tbest','fom_roi','roi']].to_csv(os.path.join(outfolder,prfolder,'all_stats.txt'))
In [261]:
sel_ind=1   #index of ROI chosed for final results

tbest=scales.iloc[sel_ind]['tbest']
roi=scales.iloc[sel_ind]['roi']
print(tbest)
allres[['tbest','fom_roi','roi']]
-0.06642458870755344
Out[261]:
tbest fom_roi roi
VK_stress_fit_crop01 0.037695 0.250484 [[-10, 10], [-45, 45]]
VK_stress_fit_crop02 -0.066425 0.493422 [[-20, 20], [-45, 45]]
VK_stress_fit_crop03 0.601099 0.755242 [[-30, 30], [-45, 45]]
VK_stress_fit_crop04 0.867663 0.795359 [[-37, 37], [-45, 45]]
VK_stress_fit_crop05 1.024602 0.844019 NaN

Plot Best

In [262]:
plt.close('all')
comp2(p1-df*tbest,s1,roi=roi)
plt.suptitle('best factor = %.3f'%tbest)
display(plt.gcf())
In [263]:
plt.savefig(os.path.join(outfolder,prfolder,'best_fit.png'))
In [264]:
from pyProfile.profile import level_profile, subtract_profiles
from pySurf.points import extract_profile

## Extract axial profile
x1,y1 = level_profile(*(extract_profile((p1-df*tbest).topoints(),(0,-45),(0,45))))
x2,y2 = level_profile(*(extract_profile(s1.topoints(),(0,-45),(0,45))))
xd,yd = level_profile(*subtract_profiles(x2,y2,x1,y1))

plt.figure()
plt.plot(x1,y1,label='PZT data with applied correction')
plt.plot(x2,y2,label='Target Shape')
plt.plot(xd,yd,label='Residuals')
plt.xlabel(p1.units[0])
plt.ylabel(p1.units[2])
plt.grid(1)
plt.ylim([-0.8,0.8])
plt.legend(loc=0)
plt.title('Simulated correction')
display(plt.gcf())
In [265]:
plt.savefig(os.path.join(outfolder,prfolder,'data_central_profile.png'))

REPEAT FIT USING VM STRESS DATA

In [266]:
prfolder='fit_VM' #partial results folder
os.makedirs(os.path.join(outfolder,prfolder),exist_ok=True)

The exact same procedure is repeated on a different set of data by Vanessa Marquez, representing a uniform stress film on front of substrate (Case2), having an integrated stress of 150 MPa um. It is then expected to find values that are about double of previous ones, but with opposite sign, since here the film is applied to front surface.

In [267]:
from pySurf.instrumentReader import FEAreader
In [268]:
FEAf2=r'G:\My Drive\Shared by Vincenzo\Metrology logs and data\Simulations\CoatingStress_Analysis\220mm_CoatingStress\Primary\220mmROC_Case1_MirrorSurfaceOnly.csv'
In [269]:
plt.figure(2)
plt.clf()

#manually adjust x and y scales to 4 in, adjsut units and signs.

fdata,xf,yf=points_autoresample(FEAreader(FEAf2))  #can accept ypix, ytox,..
#xf,yf,fdata=yf,xf,fdata.T

fdata=fdata-correct_misalignment(fdata)[0]
fdata=fdata*1000.  #m to um  
#fitsWFS_reader makes a mysterious change of sign, that is good with WK's
#data that are bump negative conversion.

#scale and center axes (100 mm square image size)
scale=1.016
xf=(xf-span(xf).sum()/2)*scale
yf=(yf-span(yf).sum()/2)*scale

df=Data2D(fdata,xf,yf,units=['mm','mm','$\mu$m'],name=os.path.basename(FEAf2))
#rule is: corners have same sign as delta Radius

plt.clf()
df.plot()
display(plt.gcf())
df.save(fn_add_subfix(FEAf2,'','.dat',strip=True,pre=outfolder+os.path.sep))
plt.savefig(fn_add_subfix(FEAf2,'','.png',strip=True,pre=outfolder+os.path.sep))
WARNING: points number doesn't match regular grid for size determined by points_find_grid
WARNING: points number doesn't match regular grid for size determined by points_find_grid
In [270]:
os.makedirs(os.path.join(outfolder,prfolder),exist_ok=True)
In [271]:
plt.close('all')

#initialize allres
scales=trioptimize(sdiff,df,fom=fom,
           rois=[[[-10,10],[-45,45]],[[-20,20],[-45,45]],[[-30,30],[-45,45]],
                 [[-37,37],[-45,45]],None],dis =True,
                  outfile=os.path.join(outfolder,prfolder,'VM_stress_fit'))
allres=allres.append(scales)


===   CASE 1: VM_stress_fit_crop01  ===

CROP: [-10.00:10.00] [-45.00:45.00]
TEST RANGE: [None None]
INITIAL FOM: 0.41725605594859444
BEST SCALE: 0.060  FOM: 0.25086072983457985



===   CASE 2: VM_stress_fit_crop02  ===

CROP: [-20.00:20.00] [-45.00:45.00]
TEST RANGE: [None None]
INITIAL FOM: 0.5969642126145148
BEST SCALE: -0.227  FOM: 0.4906235616000884



===   CASE 3: VM_stress_fit_crop03  ===

CROP: [-30.00:30.00] [-45.00:45.00]
TEST RANGE: [None None]
INITIAL FOM: 0.7835394175482431
BEST SCALE: 0.910  FOM: 0.7830138921605124



===   CASE 4: VM_stress_fit_crop04  ===

CROP: [-37.00:37.00] [-45.00:45.00]
TEST RANGE: [None None]
INITIAL FOM: 0.8747940414387781
BEST SCALE: 1.492  FOM: 0.8473941444827509



===   CASE 5: VM_stress_fit_crop05  ===

FULL APERTURE
TEST RANGE: [None None]
INITIAL FOM: 1.3916998697552254
BEST SCALE: 1.969  FOM: 0.8886228042969784

===
SUMMARY:
CROP: [-10.00:10.00] [-45.00:45.00] --> BEST SCALE:  0.060188457526431635
CROP: [-20.00:20.00] [-45.00:45.00] --> BEST SCALE:  -0.22710300976989498
CROP: [-30.00:30.00] [-45.00:45.00] --> BEST SCALE:  0.9096337851495443
CROP: [-37.00:37.00] [-45.00:45.00] --> BEST SCALE:  1.4923236547204364
FULL APERTURE--> BEST SCALE:  1.9692312407691492
=======================
In [272]:
scales[['tbest','fom_roi','roi']].to_csv(os.path.join(outfolder,prfolder,'all_stats.txt'))
In [273]:
sel_ind=1   #index of ROI chosed for final results

tbest=scales.iloc[sel_ind]['tbest']
roi=scales.iloc[sel_ind]['roi']
print(tbest)
allres[['tbest','fom_roi','roi']]
-0.22710300976989498
Out[273]:
tbest fom_roi roi
VK_stress_fit_crop01 0.037695 0.250484 [[-10, 10], [-45, 45]]
VK_stress_fit_crop02 -0.066425 0.493422 [[-20, 20], [-45, 45]]
VK_stress_fit_crop03 0.601099 0.755242 [[-30, 30], [-45, 45]]
VK_stress_fit_crop04 0.867663 0.795359 [[-37, 37], [-45, 45]]
VK_stress_fit_crop05 1.024602 0.844019 NaN
VM_stress_fit_crop01 0.060188 0.250861 [[-10, 10], [-45, 45]]
VM_stress_fit_crop02 -0.227103 0.490624 [[-20, 20], [-45, 45]]
VM_stress_fit_crop03 0.909634 0.783014 [[-30, 30], [-45, 45]]
VM_stress_fit_crop04 1.492324 0.847394 [[-37, 37], [-45, 45]]
VM_stress_fit_crop05 1.969231 0.888623 NaN

Plot Best

In [274]:
plt.close('all')
comp2(p1-df*tbest,s1)
plt.suptitle('best factor = %.3f'%tbest)
display(plt.gcf())
In [275]:
plt.savefig(os.path.join(outfolder,prfolder,'best_fit.png'))
In [276]:
from pyProfile.profile import level_profile, subtract_profiles
from pySurf.points import extract_profile

## Extract axial profile
x1,y1 = level_profile(*(extract_profile((p1-df*tbest).topoints(),(0,-45),(0,45))))
x2,y2 = level_profile(*(extract_profile(s1.topoints(),(0,-45),(0,45))))
xd,yd = level_profile(*subtract_profiles(x2,y2,x1,y1))

plt.figure()
plt.plot(x1,y1,label='PZT data with applied correction')
plt.plot(x2,y2,label='Target Shape')
plt.plot(xd,yd,label='Residuals')
plt.xlabel(p1.units[0])
plt.ylabel(p1.units[2])
plt.grid(1)
plt.ylim([-0.8,0.8])
plt.legend(loc=0)
plt.title('Simulated correction')
display(plt.gcf())
In [277]:
plt.savefig(os.path.join(outfolder,prfolder,'bestfit_profile.png'))

REPEAT FIT ON SMALLER AXIAL RANGE (VK data)

In [278]:
prfolder='fit_VK_70mm' #partial results folder
os.makedirs(os.path.join(outfolder,prfolder),exist_ok=True)

The exact same procedure is repeated on a different set of data by Vanessa Marquez, representing a uniform stress film on front of substrate (Case2), having an integrated stress of 150 MPa um. It is then expected to find values that are about double of previous ones, but with opposite sign, since here the film is applied to front surface.

In [279]:
from pySurf.instrumentReader import FEAreader
In [280]:
FEAf=r'G:\My Drive\Shared by Vincenzo\Metrology logs and data\Simulations\Coating_stress\VK20180619\coat_300_MPa_um_ur.fits'  #300 MPa*um load
In [281]:
plt.figure(2)
plt.clf()

#manually adjust x and y scales to 4 in, adjsut units and signs.

fdata,xf,yf=fitsWFS_reader(FEAf)  #can accept ypix, ytox,..
#xf,yf,fdata=yf,xf,fdata.T

fdata=fdata-correct_misalignment(fdata)[0]
fdata=-fdata*1000000.  #m to um  
#fitsWFS_reader makes a mysterious change of sign, that is good with WK's
#data that are bump negative conversion.

#scale and center axes (100 mm square image size)
nx=fdata.shape[0]
scale=101.6/nx
xf=(xf-span(xf).sum()/2)*scale
yf=(yf-span(yf).sum()/2)*scale

df=Data2D(fdata,xf,yf,units=['mm','mm','$\mu$m'],name='VK Simulation (cone)')
#rule is: corners have same sign as delta Radius

plt.clf()
df.plot()
display(plt.gcf())
In [282]:
os.makedirs(os.path.join(outfolder,prfolder),exist_ok=True)
In [283]:
from scripts.stress_compensation import comp2, scale_fit2, trioptimize, fom_rms as fom, fom_sl
from pySurf.instrumentReader import fitsWFS_reader
In [284]:
plt.close('all')

#initialize allres
scales=trioptimize(sdiff,df,fom=fom,
           rois=[[[-10,10],[-35,35]],[[-20,20],[-35,35]],[[-30,30],[-35,35]],
                 [[-37,37],[-35,35]],None],dis =True,
                  outfile=os.path.join(outfolder,prfolder,'VK_stress_fit_70mm'))
allres=allres.append(scales)


===   CASE 1: VK_stress_fit_70mm_crop01  ===

CROP: [-10.00:10.00] [-35.00:35.00]
TEST RANGE: [None None]
INITIAL FOM: 0.4487837442865901
BEST SCALE: 0.037  FOM: 0.17384083049002988



===   CASE 2: VK_stress_fit_70mm_crop02  ===

CROP: [-20.00:20.00] [-35.00:35.00]
TEST RANGE: [None None]
INITIAL FOM: 0.5346814144118338
BEST SCALE: -0.221  FOM: 0.3023393289586235



===   CASE 3: VK_stress_fit_70mm_crop03  ===

CROP: [-30.00:30.00] [-35.00:35.00]
TEST RANGE: [None None]
INITIAL FOM: 0.6492614711086595
BEST SCALE: 0.108  FOM: 0.48442880635483887



===   CASE 4: VK_stress_fit_70mm_crop04  ===

CROP: [-37.00:37.00] [-35.00:35.00]
TEST RANGE: [None None]
INITIAL FOM: 0.7022909703640965
BEST SCALE: 0.372  FOM: 0.5578904970882421



===   CASE 5: VK_stress_fit_70mm_crop05  ===

FULL APERTURE
TEST RANGE: [None None]
INITIAL FOM: 0.845737956487664
BEST SCALE: 1.025  FOM: 0.8440193362855167

===
SUMMARY:
CROP: [-10.00:10.00] [-35.00:35.00] --> BEST SCALE:  0.037251580252356836
CROP: [-20.00:20.00] [-35.00:35.00] --> BEST SCALE:  -0.2211112233761056
CROP: [-30.00:30.00] [-35.00:35.00] --> BEST SCALE:  0.10777220642790696
CROP: [-37.00:37.00] [-35.00:35.00] --> BEST SCALE:  0.37189757984276317
FULL APERTURE--> BEST SCALE:  1.0246018419416876
=======================
In [285]:
scales[['tbest','fom_roi','roi']].to_csv(os.path.join(outfolder,prfolder,'all_stats.txt'))
In [286]:
sel_ind=1   #index of ROI chosed for final results

tbest=scales.iloc[sel_ind]['tbest']
roi=scales.iloc[sel_ind]['roi']
print(tbest)
allres[['tbest','fom_roi','roi']]
-0.2211112233761056
Out[286]:
tbest fom_roi roi
VK_stress_fit_crop01 0.037695 0.250484 [[-10, 10], [-45, 45]]
VK_stress_fit_crop02 -0.066425 0.493422 [[-20, 20], [-45, 45]]
VK_stress_fit_crop03 0.601099 0.755242 [[-30, 30], [-45, 45]]
VK_stress_fit_crop04 0.867663 0.795359 [[-37, 37], [-45, 45]]
VK_stress_fit_crop05 1.024602 0.844019 NaN
VM_stress_fit_crop01 0.060188 0.250861 [[-10, 10], [-45, 45]]
VM_stress_fit_crop02 -0.227103 0.490624 [[-20, 20], [-45, 45]]
VM_stress_fit_crop03 0.909634 0.783014 [[-30, 30], [-45, 45]]
VM_stress_fit_crop04 1.492324 0.847394 [[-37, 37], [-45, 45]]
VM_stress_fit_crop05 1.969231 0.888623 NaN
VK_stress_fit_70mm_crop01 0.037252 0.173841 [[-10, 10], [-35, 35]]
VK_stress_fit_70mm_crop02 -0.221111 0.302339 [[-20, 20], [-35, 35]]
VK_stress_fit_70mm_crop03 0.107772 0.484429 [[-30, 30], [-35, 35]]
VK_stress_fit_70mm_crop04 0.371898 0.557890 [[-37, 37], [-35, 35]]
VK_stress_fit_70mm_crop05 1.024602 0.844019 NaN

Plot Best

In [287]:
plt.close('all')
comp2(p1-df*tbest,s1)
plt.suptitle('best factor = %.3f'%tbest)
display(plt.gcf())
In [288]:
plt.savefig(os.path.join(outfolder,prfolder,'best_fit.png'))
In [289]:
from pyProfile.profile import level_profile, subtract_profiles
from pySurf.points import extract_profile

## Extract axial profile
x1,y1 = level_profile(*(extract_profile((p1-df*tbest).topoints(),(0,-45),(0,45))))
#x1b,y1b = level_profile(*(extract_profile((p1-df*(-0.33)).topoints(),(0,-45),(0,45))))
x2,y2 = level_profile(*(extract_profile(s1.topoints(),(0,-45),(0,45))))
xd,yd = level_profile(*subtract_profiles(x2,y2,x1,y1))

plt.figure()
plt.plot(x1,y1,label='PZT data with applied correction')
#plt.plot(x1b,y1b,label='PZT data with center correction')
plt.plot(x2,y2,label='Target Shape')
plt.plot(xd,yd,label='Residuals')
plt.xlabel(p1.units[0])
plt.ylabel(p1.units[2])
plt.grid(1)
plt.ylim([-0.8,0.8])
plt.legend(loc=0)
plt.title('Simulated correction')
display(plt.gcf())
In [290]:
from pyProfile.profile import level_profile, subtract_profiles
from pySurf.points import extract_profile

## Extract axial profile
x1,y1 = level_profile(*(extract_profile((p1-df*tbest).topoints(),(0,-45),(0,45))))
x2,y2 = level_profile(*(extract_profile(s1.topoints(),(0,-45),(0,45))))
xd,yd = level_profile(*subtract_profiles(x2,y2,x1,y1))

plt.figure()
plt.plot(x1,y1,label='PZT data with applied correction')
plt.plot(x2,y2,label='Target Shape')
plt.plot(xd,yd,label='Residuals')
plt.xlabel(p1.units[0])
plt.ylabel(p1.units[2])
plt.grid(1)
plt.ylim([-0.8,0.8])
plt.legend(loc=0)
plt.title('Simulated correction')
display(plt.gcf())
In [291]:
plt.savefig(os.path.join(outfolder,prfolder,'bestfit_profile.png'))

Experimental fit of very narrow axial region

In [303]:
prfolder='fit_VK_narrowstrip' #partial results folder
os.makedirs(os.path.join(outfolder,prfolder),exist_ok=True)
In [304]:
plt.close('all')

#initialize allres
scales=trioptimize(sdiff,df,fom=fom,
           rois=[[[-2,2],[-35,35]]],dis =True,
                  outfile=os.path.join(outfolder,prfolder,'VK_stress_fit_narrowstrip'))
allres=allres.append(scales)


===   CASE 1: VK_stress_fit_narrowstrip_crop01  ===

CROP: [-2.00:2.00] [-35.00:35.00]
TEST RANGE: [None None]
INITIAL FOM: 0.3826289422688961
G:\My Drive\libraries\python\userKov3\pySurf\data2D.py:671: RuntimeWarning: invalid value encountered in greater
  data[((data>zrange[1]) | (data<zrange[0]))]=np.nan
G:\My Drive\libraries\python\userKov3\pySurf\data2D.py:671: RuntimeWarning: invalid value encountered in less
  data[((data>zrange[1]) | (data<zrange[0]))]=np.nan
G:\My Drive\libraries\python\userKov3\dataIO\outliers.py:28: RuntimeWarning: invalid value encountered in less
  mask=mask & (np.abs(data-np.nanmean(data))<(nsigma*sigma))
BEST SCALE: 0.216  FOM: 0.08605524321298065

===
SUMMARY:
CROP: [-2.00:2.00] [-35.00:35.00] --> BEST SCALE:  0.2164829141301822
=======================
In [305]:
scales[['tbest','fom_roi','roi']].to_csv(os.path.join(outfolder,prfolder,'all_stats.txt'))
In [308]:
sel_ind=0   #index of ROI chosed for final results

tbest=scales.iloc[sel_ind]['tbest']
roi=scales.iloc[sel_ind]['roi']
print(tbest)
allres[['tbest','fom_roi','roi']]
0.2164829141301822
Out[308]:
tbest fom_roi roi
VK_stress_fit_crop01 0.037695 0.250484 [[-10, 10], [-45, 45]]
VK_stress_fit_crop02 -0.066425 0.493422 [[-20, 20], [-45, 45]]
VK_stress_fit_crop03 0.601099 0.755242 [[-30, 30], [-45, 45]]
VK_stress_fit_crop04 0.867663 0.795359 [[-37, 37], [-45, 45]]
VK_stress_fit_crop05 1.024602 0.844019 NaN
VM_stress_fit_crop01 0.060188 0.250861 [[-10, 10], [-45, 45]]
VM_stress_fit_crop02 -0.227103 0.490624 [[-20, 20], [-45, 45]]
VM_stress_fit_crop03 0.909634 0.783014 [[-30, 30], [-45, 45]]
VM_stress_fit_crop04 1.492324 0.847394 [[-37, 37], [-45, 45]]
VM_stress_fit_crop05 1.969231 0.888623 NaN
VK_stress_fit_70mm_crop01 0.037252 0.173841 [[-10, 10], [-35, 35]]
VK_stress_fit_70mm_crop02 -0.221111 0.302339 [[-20, 20], [-35, 35]]
VK_stress_fit_70mm_crop03 0.107772 0.484429 [[-30, 30], [-35, 35]]
VK_stress_fit_70mm_crop04 0.371898 0.557890 [[-37, 37], [-35, 35]]
VK_stress_fit_70mm_crop05 1.024602 0.844019 NaN
VK_stress_fit_narrowstrip_crop01 0.216483 0.086055 [[-2, 2], [-35, 35]]

Plot Best

In [309]:
plt.close('all')
comp2(p1-df*tbest,s1)
plt.suptitle('best factor = %.3f'%tbest)
display(plt.gcf())
G:\My Drive\libraries\python\userKov3\pySurf\data2D.py:671: RuntimeWarning: invalid value encountered in greater
  data[((data>zrange[1]) | (data<zrange[0]))]=np.nan
G:\My Drive\libraries\python\userKov3\pySurf\data2D.py:671: RuntimeWarning: invalid value encountered in less
  data[((data>zrange[1]) | (data<zrange[0]))]=np.nan
G:\My Drive\libraries\python\userKov3\dataIO\outliers.py:28: RuntimeWarning: invalid value encountered in less
  mask=mask & (np.abs(data-np.nanmean(data))<(nsigma*sigma))
In [310]:
plt.savefig(os.path.join(outfolder,prfolder,'best_fit.png'))
In [311]:
from pyProfile.profile import level_profile, subtract_profiles
from pySurf.points import extract_profile

## Extract axial profile
x1,y1 = level_profile(*(extract_profile((p1-df*tbest).topoints(),(0,-45),(0,45))))
#x1b,y1b = level_profile(*(extract_profile((p1-df*(-0.33)).topoints(),(0,-45),(0,45))))
x2,y2 = level_profile(*(extract_profile(s1.topoints(),(0,-45),(0,45))))
xd,yd = level_profile(*subtract_profiles(x2,y2,x1,y1))

plt.figure()
plt.plot(x1,y1,label='PZT data with applied correction')
#plt.plot(x1b,y1b,label='PZT data with center correction')
plt.plot(x2,y2,label='Target Shape')
plt.plot(xd,yd,label='Residuals')
plt.xlabel(p1.units[0])
plt.ylabel(p1.units[2])
plt.grid(1)
plt.ylim([-0.8,0.8])
plt.legend(loc=0)
plt.title('Simulated correction')
display(plt.gcf())
In [312]:
from pyProfile.profile import level_profile, subtract_profiles
from pySurf.points import extract_profile

## Extract axial profile
x1,y1 = level_profile(*(extract_profile((p1-df*tbest).topoints(),(0,-45),(0,45))))
x2,y2 = level_profile(*(extract_profile(s1.topoints(),(0,-45),(0,45))))
xd,yd = level_profile(*subtract_profiles(x2,y2,x1,y1))

plt.figure()
plt.plot(x1,y1,label='PZT data with applied correction')
plt.plot(x2,y2,label='Target Shape')
plt.plot(xd,yd,label='Residuals')
plt.xlabel(p1.units[0])
plt.ylabel(p1.units[2])
plt.grid(1)
plt.ylim([-0.8,0.8])
plt.legend(loc=0)
plt.title('Simulated correction')
display(plt.gcf())
In [313]:
plt.savefig(os.path.join(outfolder,prfolder,'bestfit_profile.png'))

Save all stats

In [314]:
allres[['tbest','fom_roi','roi']].to_csv(os.path.join(outfolder,'all_stats.txt'))
In [302]:
from notebooktoall.transform import transform_notebook

#transform_notebook(ipynb_file=os.path.join(outfolder,os.path.basename(outfolder)+".ipynb"), 
#                   export_list=["html", "py"],file_name)
In [ ]: